/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.example.android.raycast;

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Color;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.TextView;

/**
 * View that draws, takes keystrokes, etc. for a simple RaycastActivity game.
 * 
 * updatePhysics() advances the physics based on real time. draw() renders the
 * view, and does an invalidate() to prompt another draw() as soon as possible
 * by the system.
 */
class RaycastView extends SurfaceView implements SurfaceHolder.Callback {
	
	/** Pointer to the text view to display "Paused.." etc. */
	private TextView mStatusText;
	
	/** The thread that actually draws the animation */
	private RaycastThread thread;

	private Context mContext;
	
	private final int BTN_UP = 1;
	private final int BTN_DOWN = 2;
	private final int BTN_LEFT = 3;
	private final int BTN_RIGHT = 4;
	private final int BTN_FIRE = 5;
	
	

	public RaycastView(Context context, AttributeSet attrs) {
		super(context, attrs);

		Log.w(this.getClass().getName(), "RaycastView.ctor");
		
		int startLevelNumber = 1;
		mContext = context;
		
		// register our interest in hearing about changes to our surface
		SurfaceHolder holder = getHolder();
		holder.addCallback(this);

		// create thread only; it's started in surfaceCreated()
		Log.w(this.getClass().getName(), "RaycastView.ctor creating RaycastThread");
		thread = new RaycastThread(holder, context, startLevelNumber, new Handler() {
			@Override
			public void handleMessage(Message m) {
				
				if(m.getData().getBoolean("levelComplete")) {
					
					int level = m.getData().getInt("levelNumber");
					
		            String dummy = "Level " + level + " completed!";
		            CharSequence sequence = dummy.subSequence(0, dummy.length() -1);					
					Dialog dialog = new AlertDialog.Builder(mContext)
	                .setTitle("Level Complete!")
	                .setPositiveButton("OK",new DialogInterface.OnClickListener(){
						@Override
						public void onClick(DialogInterface di, int arg1) {
							di.dismiss();
							thread.moveToNextLevel();							
						}})	                
	                .setMessage(sequence)
	                .create();					
					dialog.show();
					
				} else if(m.getData().getBoolean("playerDied")){
		            String dummy = "Game over!";
		            CharSequence sequence = dummy.subSequence(0, dummy.length() -1);					
					Dialog dialog = new AlertDialog.Builder(mContext)
	                .setTitle("FAIL")
	                .setPositiveButton("OK",new DialogInterface.OnClickListener(){
						@Override
						public void onClick(DialogInterface di, int arg1) {
							di.dismiss();
							thread.restart();
						}})	                
	                .setMessage(sequence)
	                .create();					
					dialog.show();
				}
			}
		});

		this.setOnTouchListener(controls_multitouch_listener);
	}
	


	@Override
	public boolean onTouchEvent(MotionEvent event) {
		return super.onTouchEvent(event);
	}

	/**
	 * Fetches the animation thread corresponding to this RaycastView.
	 * 
	 * @return the animation thread
	 */
	public RaycastThread getThread() {
		return thread;
	}

	/**
	 * Standard override to get key-press events.
	 */
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent msg) {
		return thread.doKeyDown(keyCode, msg);
	}

	/**
	 * Standard override for key-up. We actually care about these, so we can
	 * turn off the engine or stop rotating.
	 */
	@Override
	public boolean onKeyUp(int keyCode, KeyEvent msg) {
		return thread.doKeyUp(keyCode, msg);
	}

	/**
	 * Standard window-focus override. Notice focus lost so we can pause on
	 * focus lost. e.g. user switches to take a call.
	 */
	@Override
	public void onWindowFocusChanged(boolean hasWindowFocus) {
		if (!hasWindowFocus)
			thread.pause();
	}

	/**
	 * Installs a pointer to the text view used for messages.
	 */
	public void setTextView(TextView textView) {
		mStatusText = textView;
		mStatusText.setTextColor(Color.BLACK);
	}
	
	/**
	 * handles the multi touch control view actions Send them to the code that
	 * handles key clicks
	 */
	private OnTouchListener controls_multitouch_listener = new OnTouchListener() {

		// Declare these locally as not defined in our SDK
		final int ACTION_POINTER_INDEX_MASK = 65280;
		final int ACTION_POINTER_INDEX_SHIFT = 8;

		@Override
		public boolean onTouch(View v, MotionEvent me) {

			boolean handled = false;
			// Default KeyEvent - not used by the animation thread
			KeyEvent ke = new KeyEvent(0, 0);

			final int action = me.getAction();
			switch (action & MotionEvent.ACTION_MASK) {
			case MotionEvent.ACTION_DOWN: {
				float x = me.getX();
				float y = me.getY();

				// Given the x and y of the touch event, find out which button
				int btnId = GetButtonFromCoordinates(x, y);
				switch (btnId) {
				case BTN_UP: {
					thread.doKeyDown(KeyEvent.KEYCODE_DPAD_UP, ke);
					handled = true;
					break;
				}
				case BTN_DOWN: {
					thread.doKeyDown(KeyEvent.KEYCODE_DPAD_DOWN, ke);
					handled = true;
					break;
				}
				case BTN_LEFT: {
					thread.doKeyDown(KeyEvent.KEYCODE_DPAD_LEFT, ke);
					handled = true;
					break;
				}
				case BTN_RIGHT: {
					thread.doKeyDown(KeyEvent.KEYCODE_DPAD_RIGHT, ke);
					handled = true;
					break;
				}
				case BTN_FIRE: {
					thread.doKeyDown(KeyEvent.KEYCODE_DPAD_CENTER, ke);
					handled = true;
					break;
				}
				}

				break;
			}
			case MotionEvent.ACTION_POINTER_DOWN: {
				final int pointerIndex = (action & ACTION_POINTER_INDEX_MASK) >> ACTION_POINTER_INDEX_SHIFT;

				float x = me.getX(pointerIndex);
				float y = me.getY(pointerIndex);

				// Given the x and y of the touch event, find out which button
				int btnId = GetButtonFromCoordinates(x, y);
				switch (btnId) {
				case BTN_UP: {
					thread.doKeyDown(KeyEvent.KEYCODE_DPAD_UP, ke);
					handled = true;
					break;
				}
				case BTN_DOWN: {
					thread.doKeyDown(KeyEvent.KEYCODE_DPAD_DOWN, ke);
					handled = true;
					break;
				}
				case BTN_LEFT: {
					thread.doKeyDown(KeyEvent.KEYCODE_DPAD_LEFT, ke);
					handled = true;
					break;
				}
				case BTN_RIGHT: {
					thread.doKeyDown(KeyEvent.KEYCODE_DPAD_RIGHT, ke);
					handled = true;
					break;
				}
				case BTN_FIRE: {
					thread.doKeyDown(KeyEvent.KEYCODE_DPAD_CENTER, ke);
					handled = true;
					break;
				}
				}

				break;

			}
			case MotionEvent.ACTION_UP: {
				float x = me.getX();
				float y = me.getY();

				// Given the x and y of the touch event, find out which button
				int btnId = GetButtonFromCoordinates(x, y);
				switch (btnId) {
				case BTN_UP: {
					thread.doKeyUp(KeyEvent.KEYCODE_DPAD_UP, ke);
					handled = true;
					break;
				}
				case BTN_DOWN: {
					thread.doKeyUp(KeyEvent.KEYCODE_DPAD_DOWN, ke);
					handled = true;
					break;
				}
				case BTN_LEFT: {
					thread.doKeyUp(KeyEvent.KEYCODE_DPAD_LEFT, ke);
					handled = true;
					break;
				}
				case BTN_RIGHT: {
					thread.doKeyUp(KeyEvent.KEYCODE_DPAD_RIGHT, ke);
					handled = true;
					break;
				}
				case BTN_FIRE: {
					thread.doKeyUp(KeyEvent.KEYCODE_DPAD_CENTER, ke);
					handled = true;
					break;
				}
				}

				break;
			}
			case MotionEvent.ACTION_POINTER_UP: {
				final int pointerIndex = (action & ACTION_POINTER_INDEX_MASK) >> ACTION_POINTER_INDEX_SHIFT;

				float x = me.getX(pointerIndex);
				float y = me.getY(pointerIndex);

				// Given the x and y of the touch event, find out which button
				int btnId = GetButtonFromCoordinates(x, y);
				switch (btnId) {
				case BTN_UP: {
					thread.doKeyUp(KeyEvent.KEYCODE_DPAD_UP, ke);
					handled = true;
					break;
				}
				case BTN_DOWN: {
					thread.doKeyUp(KeyEvent.KEYCODE_DPAD_DOWN, ke);
					handled = true;
					break;
				}
				case BTN_LEFT: {
					thread.doKeyUp(KeyEvent.KEYCODE_DPAD_LEFT, ke);
					handled = true;
					break;
				}
				case BTN_RIGHT: {
					thread.doKeyUp(KeyEvent.KEYCODE_DPAD_RIGHT, ke);
					handled = true;
					break;
				}
				case BTN_FIRE: {
					thread.doKeyUp(KeyEvent.KEYCODE_DPAD_CENTER, ke);
					handled = true;
					break;
				}
				}

				break;
			}
			}

			return handled;
		}

		private int GetButtonFromCoordinates(float x, float y) {

			// Check if the gun was touched
			if (thread.getGunBitmapRect().contains((int) x, (int) y)) {
				return BTN_FIRE;
			}

			// Check if any of the controls were touched
			Rect controlsImage = thread.getArrowsBitmapRect();
			if (controlsImage.contains((int) x, (int) y)) {
				// Split the control into 9 sectors, test if any of the 4
				// containing the directional controls were touched
				int top = controlsImage.top;
				int bottom = controlsImage.bottom;
				int left = controlsImage.left;
				int right = controlsImage.right;
				int width = controlsImage.width();
				int height = controlsImage.height();
				int widthOfEachSector = width / 3;
				int heightOfEachSector = height / 3;

				// Rect(left, top, right, bottom)
				Rect upRect = new Rect(left + widthOfEachSector, top, right
						- widthOfEachSector, top + heightOfEachSector);
				Rect downRect = new Rect(left + widthOfEachSector, bottom
						- heightOfEachSector, right - widthOfEachSector, bottom);
				Rect leftRect = new Rect(left, top + heightOfEachSector, left
						+ widthOfEachSector, bottom - heightOfEachSector);
				Rect rightRect = new Rect(right - widthOfEachSector, top
						+ heightOfEachSector, right, bottom
						- heightOfEachSector);

				if (upRect.contains((int) x, (int) y)) {
					return BTN_UP;
				} else if (downRect.contains((int) x, (int) y)) {
					return BTN_DOWN;
				} else if (leftRect.contains((int) x, (int) y)) {
					return BTN_LEFT;
				} else if (rightRect.contains((int) x, (int) y)) {
					return BTN_RIGHT;
				}
			}

			// Touch was not on our gun or directional image
			return 0;
		}
	};

	/* Callback invoked when the surface dimensions change. */
	public void surfaceChanged(SurfaceHolder holder, int format, int width,
			int height) {
		thread.setSurfaceSize(width, height);
		//This needs to be called at least once, and we cant start until we know
		//the dimensions of the surface, so start the thread from here
		Log.w(this.getClass().getName(), "RaycastView.surfaceChanged calling thread.setRunning true");
		thread.setRunning(true);
		if(!thread.isAlive()) {
			thread.start();
		}
	}

	/*
	 * Callback invoked when the Surface has been created and is ready to be
	 * used.
	 */
	public void surfaceCreated(SurfaceHolder holder) {
		// start the thread here so that we don't busy-wait in run()
		// waiting for the surface to be created
		//thread.setRunning(true);
		//thread.start();
	}

	/*
	 * Callback invoked when the Surface has been destroyed and must no longer
	 * be touched. WARNING: after this method returns, the Surface/Canvas must
	 * never be touched again!
	 */
	public void surfaceDestroyed(SurfaceHolder holder) {
		// we have to tell thread to shut down & wait for it to finish, or else
		// it might touch the Surface after we return and explode
		boolean retry = true;
		thread.setRunning(false);
		while (retry) {
			try {
				thread.join();
				retry = false;
			} catch (InterruptedException e) {
			}
		}
	}
}
